package pl.streamsoft.test.model;

import java.util.Date;
import java.util.List;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.tomcat.dbcp.dbcp2.BasicDataSource;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
import org.springframework.orm.hibernate4.HibernateTransactionManager;
import org.springframework.orm.hibernate4.LocalSessionFactoryBean;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import pl.streamsoft.test.model.entities.CurrencyAverageEntity;
import pl.streamsoft.test.model.entities.CurrencyEntity;
import pl.streamsoft.test.model.entities.CurrencyTypeEntity;
import pl.streamsoft.test.model.entities.DateEntryEntity;
import pl.streamsoft.test.model.exceptions.DAOException;

@Configuration
@Component("CurrenciesHibernateBean")
@PropertySource({ "postgresql.config.properties" })
@EnableTransactionManagement
public class CurrenciesHibernate implements CurrenciesDAO {

	@Autowired
	private SessionFactory sesionFactory;

	/**
	 * Configuration
	 */
	@Autowired
	private Environment env;

	@Bean
	public LocalSessionFactoryBean sessionFactory() {
		System.out.println("Creating SessionFactory Object in " + this);
		LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
		sessionFactory.setDataSource(restDataSource());
		sessionFactory
				.setPackagesToScan(new String[] { "pl.streamsoft.test.model.entities" });
		sessionFactory
				.setMappingResources(new String[] {
						"pl/streamsoft/test/model/entities/CurrencyEntity.hbm.xml",
						"pl/streamsoft/test/model/entities/CurrencyTypeEntity.hbm.xml",
						"pl/streamsoft/test/model/entities/CurrencyAverageEntity.hbm.xml",
						"pl/streamsoft/test/model/entities/DateEntryEntity.hbm.xml" });
		sessionFactory.setHibernateProperties(hibernateProperties());

		return sessionFactory;
	}

	@Bean
	public HibernateTransactionManager transactionManager() {
		HibernateTransactionManager txManager = new HibernateTransactionManager();
		txManager.setSessionFactory(sessionFactory().getObject());

		return txManager;
	}

	@Bean
	public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
		return new PersistenceExceptionTranslationPostProcessor();
	}

	Properties hibernateProperties() {
		return new Properties() {
			private static final long serialVersionUID = 1L;

			{
				setProperty("hibernate.hbm2ddl.auto", "create-drop");
				setProperty("hibernate.show_sql", "true");
				setProperty("hibernate.dialect",
						"org.hibernate.dialect.MySQL5Dialect");
				setProperty("hibernate.globally_quoted_identifiers", "true");

				setProperty("hibernate.dialect",
						"org.hibernate.dialect.PostgreSQLDialect");
				setProperty("hibernate.show_sql", "true");
				setProperty("hibernate.format_sql", "false");
				setProperty("hibernate.query.startup_check", "true");
				setProperty("hibernate.cache.use_query_cache", "true");
				setProperty("hibernate.cache.use_second_level_cache", "true");
				setProperty("hibernate.cache.region.factory_class",
						"org.hibernate.cache.ehcache.EhCacheRegionFactory");
			}
		};
	}

	@Bean
	public DataSource restDataSource() {
		BasicDataSource dataSource = new BasicDataSource();

		dataSource.setDriverClassName("org.postgresql.Driver");
		dataSource.setUrl("jdbc:postgresql://192.168.72.121/staging");
		dataSource.setUsername("postgres");
		dataSource.setPassword("postgres");

		return dataSource;
	}

	@Bean
	@Primary
	public PlatformTransactionManager txManager() throws Exception {
		HibernateTransactionManager txManager = new HibernateTransactionManager(
				sessionFactory().getObject());
		txManager.setNestedTransactionAllowed(true);

		return txManager;
	}

	/**
	 * Configuaration ends
	 */

	public CurrenciesHibernate() {
		System.out.println("CurrenciesHibernate::constructor");
	}

	public Session getSession() {
		return this.sesionFactory.getCurrentSession();
	}

	public void beginTransaction() throws DAOException {
	}

	public void commitTransaction() throws DAOException {
	}

	public void rollbackTransaction() throws DAOException {
	}

	public CurrencyEntity insert(CurrencyEntity entity) throws DAOException {
		if (entity.getType() == null)
			throw new DAOException("Inserting failed. No currency type defined");
		if (entity.getDate() == null)
			throw new DAOException("Inserting failed. No date defined");

		getSession().persist(entity);
		return entity;
	}

	public CurrencyEntity update(CurrencyEntity entity) throws DAOException {
		getSession().merge(entity);
		return entity;
	}

	public CurrencyEntity delete(CurrencyEntity entity) throws DAOException {
		getSession().delete(entity);
		return entity;
	}

	public CurrencyEntity findCurrency(Integer id) throws DAOException {
		return (CurrencyEntity) getSession().get(CurrencyEntity.class, id);
	}

	public List<CurrencyEntity> findAllCurrencies(CurrencyTypeEntity typeEntity)
			throws DAOException {
		Query query = getSession().getNamedQuery("findAllCurrenciesByType");
		query.setParameter("type", typeEntity);

		List<CurrencyEntity> list = query.list();
		return list;
	}

	public List<CurrencyEntity> findAllCurrencies(
			CurrencyTypeEntity typeEntity, int limit, int offset)
			throws DAOException {
		Query query = getSession().getNamedQuery(
				"findAllCurrenciesByTypeLimited");
		query.setParameter("currencyType", typeEntity);
		query.setMaxResults(limit);
		query.setFirstResult(offset);

		List<CurrencyEntity> list = query.list();
		return list;
	}

	public List<CurrencyEntity> findCurrenciesByDate(Date from, Date to)
			throws DAOException {
		Query query = getSession().getNamedQuery("findAllCurrenciesByDate");
		query.setParameter("from", from);
		query.setParameter("to", to);

		List<CurrencyEntity> list = query.list();
		return list;
	}

	public List<CurrencyEntity> findCurrenciesByDateAndType(Date from, Date to,
			CurrencyTypeEntity typeEntity) throws DAOException {
		Query query = getSession().getNamedQuery(
				"findAllCurrenciesByDateAndType");
		query.setParameter("type", typeEntity);
		query.setParameter("from", from);
		query.setParameter("to", to);

		List<CurrencyEntity> list = query.list();
		return list;
	}

	public CurrencyTypeEntity insert(CurrencyTypeEntity entity)
			throws DAOException {
		getSession().persist(entity);
		return entity;
	}

	public CurrencyTypeEntity update(CurrencyTypeEntity entity)
			throws DAOException {
		getSession().merge(entity);
		return entity;
	}

	public CurrencyTypeEntity delete(CurrencyTypeEntity entity)
			throws DAOException {
		getSession().delete(entity);
		return entity;
	}

	public CurrencyTypeEntity findCurrencyType(int id) throws DAOException {
		CurrencyTypeEntity currencyType = (CurrencyTypeEntity) getSession()
				.get(CurrencyEntity.class, id);
		if (currencyType == null) {
			throw new DAOException("Currency type not found");
		}
		return currencyType;
	}

	public CurrencyTypeEntity findCurrencyType(String acronym)
			throws DAOException {
		Query query = getSession().getNamedQuery(
				"findAllCurrencyTypesByAcronym");
		query.setParameter("acronym", acronym);

		CurrencyTypeEntity currencyType = (CurrencyTypeEntity) query
				.uniqueResult();
		if (currencyType == null) {
			throw new DAOException("Currency type not found");
		}
		return currencyType;
	}

	public List<CurrencyTypeEntity> findAllCurrencyTypes() throws DAOException {
		Query query = getSession().getNamedQuery("findAllCurrencyTypes");
		return (List<CurrencyTypeEntity>) query.list();
	}

	public DateEntryEntity insert(DateEntryEntity entity) throws DAOException {
		getSession().persist(entity);
		return entity;
	}

	public DateEntryEntity update(DateEntryEntity entity) throws DAOException {
		getSession().merge(entity);
		return entity;
	}

	public DateEntryEntity delete(DateEntryEntity entity) throws DAOException {
		getSession().delete(entity);
		return entity;
	}

	public DateEntryEntity findDateEntry(int id) throws DAOException {
		DateEntryEntity dateEntry = (DateEntryEntity) getSession().get(
				CurrencyEntity.class, id);
		if (dateEntry == null) {
			throw new DAOException("Date not found");
		}
		return dateEntry;
	}

	public DateEntryEntity findDateEntry(Date date) throws DAOException {
		Query query = getSession().getNamedQuery("findDateEntryByDate");
		query.setCacheable(true);
		query.setParameter("date", date);
		DateEntryEntity dateEntry = (DateEntryEntity) query.uniqueResult();
		if (dateEntry == null) {
			throw new DAOException("Date not found");
		}
		return dateEntry;
	}

	public List<DateEntryEntity> findAllDateEntries() throws DAOException {
		// TODO Auto-generated method stub
		return null;
	}

	public CurrencyAverageEntity insert(CurrencyAverageEntity entity)
			throws DAOException {
		getSession().persist(entity);
		return entity;
	}

	public CurrencyAverageEntity update(CurrencyAverageEntity entity)
			throws DAOException {
		getSession().merge(entity);
		return entity;
	}

	public CurrencyAverageEntity delete(CurrencyAverageEntity entity)
			throws DAOException {
		getSession().delete(entity);
		return entity;
	}

	public CurrencyAverageEntity findCurrencyAverage(int id)
			throws DAOException {
		return (CurrencyAverageEntity) getSession().get(
				CurrencyAverageEntity.class, id);
	}

	public CurrencyAverageEntity findCurrencyAverage(
			CurrencyTypeEntity currencyTypeEntity) throws DAOException {
		Query query = getSession().getNamedQuery("findCurrencyAverage");
		query.setParameter("type", currencyTypeEntity);
		CurrencyAverageEntity currencyAverage = (CurrencyAverageEntity) query
				.uniqueResult();
		if (currencyAverage == null) {
			throw new DAOException("CurrencyAverageEntity not found.");
		}

		return currencyAverage;
	}

}
